import traceback
import uuid
import numpy as np

from common import constants
from common.message import MessageContent
from db.oss.oss_service import ObjectStorage
import os
from util_tool.draw_picture import DrawPicture
from util_tool.conversation_util import ConversationUtil
import asyncio
from bot.insurance_planner_gpt.user_info import UserInfo
from common.log import logger

project_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
IMG_PATH = project_path + '/file/'

def extract_user_info(sentence):
    res_info = {'current_age': 30,
                'expect_retire_age': 50,
                'retire_cost': 15000,
                # 'retire_reserve_fund': 20000,
                'sex': '女',
                # 'start_security_age': 22,
                'total_revenue': 20000,
                'total_expenditure': 10000,
                'have_social_security': '否'

                }
    if len(sentence) == 0:
        return res_info
    # TODO: 调用大模型 从用户语音sentence抽取用户基本信息
    model = {}
    if len(model) > 0:
        for i in res_info:
            if i in model:
                res_info[i] = model[i]
            else:
                logger.info('user_info %s not in model result, use default value!' % i)
    return res_info


class Solution(object):
    plan_summary_dict = {"legal_retirement_age": "法定退休年龄", "gap_fund_accumulate": "退休资金累积缺口",
                         "res_max_age_fund": "退休前需存储资金", "yearly_income_surplus": "每年收入需要结余",
                         "res_surplus": "退休前收入盈余"}
    def __init__(self, user_info, session_id, familyid="lingxi"):
        logger.info('{} userInfo: {}'.format(familyid, user_info))

        self.uid = familyid
        self.user_info = user_info
        self.user_info_cn = UserInfo.get_chinese_dict(user_info)
        self.session_id = session_id
        self.current_age = user_info.get('current_age')  # 客户当前年龄   30
        self.expect_retire_age = user_info.get('expect_retire_age', None)  # 客户预计退休年龄   50
        self.retire_cost = user_info.get('retire_cost')  # 客户退休后(月)花销   20000
        self.retire_reserve_fund = user_info.get('retire_reserve_fund', 0)  # 客户为退休准备资金   300000
        self.sex = user_info.get('sex', '未知')

        if (self.sex == '女') or (self.sex == '女性') or (self.sex == "woman"):
            self.pension_age = constants.LAW_RETIRE_AGE_W  # 客户可以领养老金年龄 默认设定   65
        elif (self.sex == '男') or (self.sex == '男性') or (self.sex == "man"):
            self.pension_age = constants.LAW_RETIRE_AGE_M
        else:
            self.pension_age = None

        self.start_security_num = user_info.get('start_security_num', None)  # 客户开始交社保年限
        self.start_security_age = user_info.get('start_security_age', None)  # 客户开始交社保年龄，兜底25岁

        self.have_social_security = user_info.get('have_social_security', None)  # 客户是否交过社保   默认没有

        self.total_revenue = user_info.get('total_revenue', None)  # 客户年总收入   100000

        self.total_expenditure = user_info.get('total_expenditure', -1)  # 客户年总支出   50000

        self.inflation_rate = user_info.get('inflation_rate',None)  # 通货膨胀率 默认设定   3%
        if self.inflation_rate is not None:
            self.inflation_rate = self.inflation_rate/100
        else:
            self.inflation_rate = constants.INFLATION_RATE

        self.financial_management = user_info.get('financial_management', None)  # 客户理财能力 默认设定   4%
        if self.financial_management is not None:
            self.financial_management = self.financial_management/100
        else:
            if self.inflation_rate<=0:
                self.financial_management = 0
            else:
                self.financial_management = constants.DEFAULT_FINANCIAL_MANAGEMENT

        self.income_growth_rate = user_info.get('income_growth_rate', None)  # 退休前收入涨幅
        if self.income_growth_rate is not None:
            self.income_growth_rate = self.income_growth_rate/100
        else:
            if self.inflation_rate <= 0:
                self.income_growth_rate = 0
            else:
                self.income_growth_rate = constants.DEFAULT_INCOME_GROWTH_RATE

        self.persion_rise_rate = user_info.get('persion_rise_rate', None)  # 养老金收入涨幅
        if self.persion_rise_rate is not None:
            self.persion_rise_rate = self.persion_rise_rate/100
        else:
            if self.inflation_rate <= 0:
                self.persion_rise_rate = 0
            else:
                self.persion_rise_rate = constants.DEFAULT_PENSION_RISE_RATE

        self.max_age = user_info.get('max_age',  constants.DEFAULT_MAX_LIVE_AGE) # 假定生命周期 默认设定   85 =====> 上界120

        # self.load_config_param()
        self.repair_user_info()

        logger.info('{},step 0.0.0 客户基本信息：当前年龄: {},预计退休年龄：{},假定生命周期：{},性别：{}，领取社保养老金年龄：{},退休前收入：{},退休前收入涨幅：{},'
                    '退休前支出：{},退休后支出：{},养老金涨幅：{},通货膨胀率：{},理财能力：{},退休准备资金：{},开始交社保年龄：{},是否交过社保：{}'
                    .format(self.uid,self.current_age,self.expect_retire_age,self.max_age,self.sex,self.pension_age,self.total_revenue,
                            self.income_growth_rate,self.total_expenditure,self.retire_cost,self.persion_rise_rate,
                            self.inflation_rate,self.financial_management,self.retire_reserve_fund,self.start_security_age,
                            self.have_social_security))

        self.oss = ObjectStorage(bucket_name="planner")
        self.cu = ConversationUtil()

        self.total_revenue_before_retire = {}
        self.total_revenue_after_retire = {}

        #记录重叠部分收入
        self.overlap_revenue = {}
        logger.info('init end!')

    def repair_user_info(self):
        # 默认退休年龄=法定退休年龄
        if not self.expect_retire_age and self.pension_age:
            self.expect_retire_age = self.pension_age
        if self.expect_retire_age and self.pension_age and self.expect_retire_age > self.pension_age:
            self.expect_retire_age = self.pension_age

        if self.start_security_num or self.start_security_age:
            self.have_social_security = '是'

        if not self.start_security_num and not self.start_security_age and self.have_social_security == '是':
            self.start_security_age = constants.DEFAULT_START_SECURITY_AGE

        if self.start_security_num is not None and self.start_security_num != 0 and self.current_age is not None:
            if self.start_security_age is None:
                self.start_security_age = self.current_age - self.start_security_num
        elif self.start_security_age is not None and self.current_age is not None:
            if self.start_security_num is None:
                self.start_security_num = self.current_age - self.start_security_age

    def check_param(self, cost_maxBound=2000000, fund_maxBound=1000000000):
        # 参数合法性校验
        all_ok = True
        if self.inflation_rate >= 1:
            all_ok = False
            logger.info('Illegal inflation_rate!')
        elif all_ok and self.financial_management >= 1:
            all_ok = False
            logger.info('Illegal financial_management!')
        # elif all_ok and self.pension_age >= self.max_age:
        #     all_ok = False
        #     logger.info('Illegal pension_age!')
        elif all_ok and self.max_age >= 120:
            all_ok = False
            logger.info('Illegal max_age!')
        elif all_ok and not isinstance(self.uid, str):
            all_ok = False
            logger.info('Illegal uid!')
        elif all_ok and not self.have_social_security :
            all_ok = False
            logger.info('Illegal have_social_security!')
        elif all_ok and not self.start_security_num and not self.start_security_age and self.have_social_security=='是':
            all_ok = False
            logger.info('Illegal start_security_num! or start_security_age! start_security_num:{},start_security_age:{}'.format(self.start_security_num,self.start_security_age))
        elif all_ok and self.sex not in set(['男', '女', 'woman', 'man', '男性', '女性']):
            all_ok = False
            logger.info('Illegal sex!')
        # elif all_ok and self.current_age >= self.pension_age:
        #     all_ok = False
        #     logger.info('Illegal current_age!')
        # elif all_ok and self.expect_retire_age > self.pension_age:
        #     self.expect_retire_age = self.pension_age
        #     # all_ok = False
        #     # logger.info('Illegal expect_retire_age!')
        elif all_ok and self.start_security_age and self.pension_age and self.start_security_age > self.pension_age:
            all_ok = False
            logger.info('Illegal start_security_age!')
        elif all_ok and not self.retire_cost:
            if self.total_expenditure and self.total_expenditure > 0:
                self.retire_cost = self.total_expenditure
            else:
                all_ok = False
                logger.info('Illegal retire_cost!')
        # elif all_ok and (not self.total_revenue or self.total_revenue<=0):
        #     all_ok = False
        #     logger.info('Illegal retire_reserve_fund!')
        # elif all_ok and self.total_revenue < 0:
        #     all_ok = False
        #     logger.info('Illegal retire_reserve_fund!')
        elif all_ok and self.retire_reserve_fund < 0:
            all_ok = False
            logger.info('Illegal retire_reserve_fund!')

        self.cost_maxBound = cost_maxBound
        if self.retire_cost and self.retire_cost > self.cost_maxBound:
            logger.info('Attention: User need Huge retire_cost: %d > %d' %
                  (self.retire_cost, self.cost_maxBound))
        self.fund_maxBound = fund_maxBound
        if self.retire_reserve_fund and self.fund_maxBound and self.retire_reserve_fund > self.fund_maxBound:
            logger.info('Attention: User has Enough retire_reserve_fund: %d > %d' %
                  (self.retire_reserve_fund, self.fund_maxBound))

        return all_ok  # 返回False 即存在不合法参数

    def cost_everyyear(self):
        """
        step 1.0: 从客户预计退休年龄 到假定生命周期 每年的花销
        退休第k年生活花销 = 退休后的月花销*12 * (1+通货膨胀率)^(预计退休年龄+k-1-当前年龄)
        """
        res_cost = {}
        res_cost_show = {}
        try:
            if self.expect_retire_age is not None:
                for k in range(self.expect_retire_age, self.max_age + 1):
                    year_k = k - self.expect_retire_age + 1  # 退休第k年
                    year_cost = self.retire_cost * 12 * (1 + self.inflation_rate) ** \
                                (self.expect_retire_age + year_k - 1 - self.current_age)
                    res_cost[k] = year_cost
                    res_cost_show[k] = round(year_cost)
        except Exception as ee:
            pass
        logger.info('{},{} 到 {} 每年花销: {}'.format(self.uid,self.expect_retire_age,self.max_age,res_cost_show))
        return res_cost, res_cost_show

    def prepare_pension_firstyear(self, avg_social_wage=10628, person_wage_level=10000, devisor_factor=139):
        """
        step 1.1: 从客户可以领养老金年龄开始, 客户可以领第一年的已备养老金;
        先根据用户「个人月收入」确定用户领取哪一档养老金；确定领取哪一档后，根据「个人缴费年限」和「计发月数」, 计算「月领取养老金」.
        avg_social_wage: 退休前社会平均工资
        person_wage_level: 个人月收入 对应的档位值
        devisor_factor: 计发月数, {退休年纪:计发月数}, e.g.{55: 170, 60: 139, 65: 101}
        """
        if not self.pension_age or not self.expect_retire_age or not self.start_security_age:
            return 0

        if self.pension_age==constants.LAW_RETIRE_AGE_W:
            devisor_factor = 139
        elif self.pension_age==constants.LAW_RETIRE_AGE_M:
            devisor_factor = 101

          # 个人缴费年限, 客户预计退休年龄 - 客户开始交社保年龄   预计退休年龄>法定退休年龄时，交社保截止年龄=法定退休年龄

        if self.expect_retire_age > self.pension_age:
            person_pay_year = self.pension_age - self.start_security_age
        else:
            person_pay_year = self.expect_retire_age - self.start_security_age

        logger.info('{} 退休时个人社保缴费年限: {}'.format(self.uid,person_pay_year))
        if person_pay_year<constants.MIN_PERSON_PAY_YEAR:
            return 0

        # 基础养老金 C3 * (D3 / C3 + 1) / 2 * E3 * 1 %
        cur_base = avg_social_wage * (person_wage_level / avg_social_wage + 1) / 2 * person_pay_year * 0.01
        # 个人养老金 D3 * 8% * 12 * E3 / F3
        cur_person = person_wage_level * 0.08 * 12 * person_pay_year / devisor_factor
        cur_month_pension = cur_base + cur_person
        gap_year = self.pension_age - self.current_age
        future_month_pension = cur_month_pension * (1 + self.persion_rise_rate) ** gap_year  # 月领取养老金
        future_year_pension = future_month_pension * 12  # 年领取养老金
        logger.info('{} {} 岁时社会养老金 {}'.format(self.uid,self.pension_age,future_year_pension))
        return future_year_pension

    def prepare_pension_everyyear(self):
        """
        step 1.1: 从客户可以领养老金年龄 到假定生命周期 每年的已备养老金
        可领养老金的第k年 = 月领取养老金*12 * (1+养老金收入涨幅)^(可以领养老金年龄65-当前年龄+k-1)
        """
        res_pension = {}
        res_pension_show = {}
        first_year = 0
        if self.have_social_security and self.have_social_security=='否':
            for k in range(self.expect_retire_age,self.max_age+1):
                res_pension[k] = 0
                res_pension_show[k] = 0
        else:
            # if self.have_social_security == '是':
            #     # calculation = SocialSecurityCalculation(user_info=self.user_info)
            #     # calculation.prepare_pension_firstyear()
            #     # first_year = calculation.user_info.get('pension_result',None)
            #     # if first_year is None or first_year==0:
            #     #     first_year = calculation.user_info.get('personal_pension_result',None)
            #     # else:
            #     #     first_year = round(first_year) * 12
            #     first_year = self.prepare_pension_firstyear()
            #     if first_year is not None:
            #         first_year = round(first_year)
            #         if (self.expect_retire_age - self.start_security_age)<15:
            #             res_pension[self.pension_age] = first_year
            #             res_pension_show[self.pension_age] = first_year
            #             for age in range(self.pension_age+1,self.max_age+1):
            #                 res_pension[age] = 0
            #                 res_pension_show[age] = 0
            #         else:
            #             res_pension[self.pension_age] = first_year
            #             res_pension_show[self.pension_age] = round(first_year)
            #
            #             for k in range(self.pension_age + 1, self.max_age + 1):
            #                 year_k = k - self.pension_age  # 可以领养老金第k年
            #                 year_pension = first_year * (1 + self.persion_rise_rate) ** year_k
            #                 res_pension[k] = year_pension
            #                 res_pension_show[k] = round(year_pension)
            if self.have_social_security == '是':
                first_year = self.prepare_pension_firstyear()

            res_pension[self.pension_age] = first_year
            res_pension_show[self.pension_age] = round(first_year)

            for k in range(self.pension_age + 1, self.max_age + 1):
                year_k = k - self.pension_age  # 可以领养老金第k年
                year_pension = first_year * (1 + self.persion_rise_rate) ** year_k
                res_pension[k] = year_pension
                res_pension_show[k] = round(year_pension)

            self.total_revenue_after_retire = res_pension_show

            logger.info('{} step 1.1: 已备养老金:{}'.format(self.uid,str(res_pension_show)))

            if not self.overlap_revenue:
                self.count_overlap_revenue()
        return res_pension, res_pension_show

    # 退休当年资产规模
    def prepare_fund_scale(self, n, financial_management=None):
        if n<=0 or self.total_revenue is None or not self.total_expenditure:
            return -1
        """
        step 1.2: 退休当年的资产规模, 根据当前 客户为退休准备资金 计算
        退休准备资金 * (1+客户理财能力)^(预计退休年龄-当前年龄)
        """
        financial_management = financial_management if financial_management else self.financial_management
        if n == 1:
            F_1 = self.retire_reserve_fund * (1 + financial_management) + (
                    self.total_revenue - self.total_expenditure) * 12
            logger.info('{} {} 年资产增量:{}'.format(self.uid,n,F_1))
            return F_1
        else:
            '''
            第n年的资产规模=第n-1年的资产规模*（1+理财收益率）+第n年的结余
            第n年的结余=第n年的收入-第n年的支出
            第n年的收入=第1年的收入*（1+通货膨胀率）^（n-1）
            '''
            jyN = (self.total_revenue * 12) * (1 + self.income_growth_rate) ** (n - 1) - \
                  (self.total_expenditure * 12) * (1 + self.inflation_rate) ** (n - 1)

            res_fund_scale = (1 + financial_management) * self.prepare_fund_scale(n - 1,financial_management) + jyN
            logger.info('{} {} 年资产增量:{}'.format(self.uid, n, res_fund_scale))
            return res_fund_scale

    def compute_res_fund(self,income_after_retire, necessary_fund_scale, retire_expense_monthly_reduce_to=None):
        """
        step 2.0: 计算从客户预计退休年龄 到假定生命周期 每年的剩余资产
        退休当年剩余资产 = (退休当年的资产规模+资产规模) * (1+客户的理财能力) - (当年花销部分-当年已备部分)
        退休第k年剩余资产 = 退休第k-1年剩余资产 * (1+客户的理财能力) - (退休第k年花销部分-退休第k年已备部分)
        """
        # res_fund_scale = self.prepare_fund_scale(self.expect_retire_age - self.current_age)  # 预计退休 当年的资产规模


        res_remain_fund = {}
        res_remain_fund_show = {}

        if self.expect_retire_age is None:
            return res_remain_fund, res_remain_fund_show

        retire_expense_monthly_reduce_to = retire_expense_monthly_reduce_to and retire_expense_monthly_reduce_to or self.retire_cost

        for k in range(self.expect_retire_age, self.max_age + 1):
            year_k1 = k - self.expect_retire_age + 1  # 预计退休 第k年
            year_cost = retire_expense_monthly_reduce_to * 12 * (1 + self.inflation_rate) ** \
                        (self.expect_retire_age + year_k1 - 1 - self.current_age)  # 预计退休 第k年花销部分

            year_income = 0
            if income_after_retire:
                year_income = income_after_retire.get(k,0)

            if k == self.expect_retire_age:  # 预计退休 当年
                res_remain_fund[k] = (necessary_fund_scale) * (1 + self.financial_management) - (
                        year_cost - year_income)
                res_remain_fund_show[k] = round(res_remain_fund[k])
            else:
                res_remain_fund[k] = res_remain_fund[k - 1] * (1 + self.financial_management) - (
                        year_cost - year_income)
                res_remain_fund_show[k] = round(res_remain_fund[k])
        return res_remain_fund, res_remain_fund_show

    def find_necessary_fund_scale(self, fund_scale_Min, fund_scale_Max, retire_expense_monthly_reduce_to=None):
        """
        通过 到假定生命周期那年的剩余资产, 反推 用户预计退休时的资产规模, 该值的取值 归约为整10万;
        max_age_res_fund: 到达假定生命周期时, 用户的剩余资产, 该值的取值范围在200000以内
        """

        income_after_retire = {}
        if self.have_social_security and self.have_social_security == '是':
            _,income_after_retire = self.prepare_pension_everyyear()  # 法定退休年龄-生命周期，每年收入
            if self.overlap_revenue and self.pension_age and self.expect_retire_age and \
                    self.expect_retire_age > self.pension_age:
                for ageNum in range(self.pension_age,self.expect_retire_age):
                    income_after_retire[ageNum] = self.overlap_revenue.get(ageNum, 0)


        interval = 10000
        fund_list = [i for i in range(fund_scale_Min, fund_scale_Max, interval)]
        left, right = 0, len(fund_list)

        necessary_fund_scale = None
        while left <= right:
            mid = left + (right - left) // 2
            if mid >= len(fund_list):
                return fund_scale_Max
            if right < 1:
                return fund_list[1]
            necessary_fund_scale = fund_list[mid]
            res_fund, res_fund_show = self.compute_res_fund(income_after_retire,necessary_fund_scale, retire_expense_monthly_reduce_to)

            # 确保不存在某个月的花销低于0
            compute_max_age_res_fund = min(res_fund.values())
            # logger.info('left:{},right:{},necessary_fund_scale:{},compute_max_age_res_fund:{}'.format(left, right,
            #                                                                                           necessary_fund_scale,
            #                                                                                           compute_max_age_res_fund))
            if compute_max_age_res_fund>0 and compute_max_age_res_fund<10000:
                return necessary_fund_scale

            if 0 < compute_max_age_res_fund:
                right = mid - 1  # 剩余资产 过多
            else:  # 剩余资产 过少
                left = mid + 1

        return necessary_fund_scale

    def find_necessary_fund_scale_slidewindow(self, slidewide=30000000, retire_expense_monthly_reduce_to=None):
        '''
        退休前应备资金
        '''
        if self.expect_retire_age is not None:
            fund_scale_Min, fund_scale_Max = 0, 30000000
            while True:
                fund_scale = self.find_necessary_fund_scale(fund_scale_Min, fund_scale_Max,
                                                            retire_expense_monthly_reduce_to)
                if fund_scale < fund_scale_Max:
                    break
                else:
                    fund_scale_Max = fund_scale_Max * 2

            logger.info('{} step 2.0: 退休前应备资产 {}'.format(self.uid,str(fund_scale)))
            return fund_scale
        else:
            return None

    def _convert_when(self, when):
        _when_to_num = {'end': 0, 'begin': 1, 'e': 0, 'b': 1, 0: 0, 1: 1, 'beginning': 1, 'start': 1, 'finish': 0}
        if isinstance(when, np.ndarray):
            return when
        try:
            return _when_to_num[when]
        except (KeyError, TypeError):
            return [_when_to_num[x] for x in when]

    def compute_PMT(self, pv, fv=0, time='end'):
        """
        step 3.0: 根据利率、付款期数、资产规模, 计算每期的资产准备额度; 参考1.19版本numpy包的pmt()函数逻辑, 逻辑正确性已验证
        pv: 现值, 这里取0
        fv: 未来值, 这里取 资产规模
        time: 还款在0期末 or 1期初
        """
        rate = self.financial_management  # 利率, 即 客户理财能力

        # 预计退休年龄 默认法定65岁 当前年龄 默认30岁
        nper = (self.expect_retire_age if self.expect_retire_age else self.pension_age) - (
            self.current_age if self.current_age else 30)  # 付款期数, 即 需要还款的年数
        when = self._convert_when(time)  # 确定 0期末 or 1期初
        (rate, nper, pv, fv, when) = map(np.array, [rate, nper, pv, fv, when])
        temp = (1 + rate) ** nper
        mask = (rate == 0)
        masked_rate = np.where(mask, 1, rate)
        fact = np.where(mask != 0, nper, (1 + masked_rate * when) * (temp - 1) / masked_rate)
        return -(fv + pv * temp) / fact

    def cost_everyyear_before_retire(self, current_expenditure=None):
        """
        step 1.0: 计算退休前的花销
            当前开始 到客户期望的退休年龄 每年的花销
        第k年生活花销 = 当前年支出 * (1+通货膨胀率)^(k-1-当前年龄)
        """
        current_expenditure = current_expenditure if current_expenditure else self.total_expenditure

        res_cost = {}
        res_cost_show = {}
        if self.expect_retire_age and self.total_expenditure >= 0:
            for k in range(self.current_age, self.expect_retire_age):
                year_cost = current_expenditure * 12 * (1 + self.inflation_rate) ** (k - self.current_age)
                res_cost[k] = year_cost
                res_cost_show[k] = round(year_cost)
        logger.info('{} step 0.0: 退休前花销部分: {}'.format(self.uid,str(res_cost_show)))
        return res_cost, res_cost_show

    def prepare_pension_everyyear_before_retire(self, current_revenue=None):
        """
        step 1.1.1: 计算退休前的收入
            当前开始 到客户期望的退休年龄 每年的收入
        第k年生活收入= 当前年收入 * (1+收入涨幅)^(k-1-当前年龄)
        """
        current_revenue = current_revenue if current_revenue else self.total_revenue
        res_pension = {}
        res_pension_show = {}
        if current_revenue and self.expect_retire_age:
            for k in range(self.current_age, self.expect_retire_age):
                year_pension = current_revenue * 12 * (1 + self.income_growth_rate) ** (k - self.current_age)
                res_pension[k] = year_pension
                res_pension_show[k] = round(year_pension)

        self.total_revenue_before_retire = res_pension_show
        logger.info('{} step 1.2: 退休前收入:{}'.format(self.uid,str(res_pension_show)))
        if not self.overlap_revenue:
            self.count_overlap_revenue()
        return res_pension, res_pension_show

    def compute_fund_scale_everyyear_before_retire(self, current_pension, current_expenditure,
                                                   pension_everyyear_before_retire, cost_everyyear_before_retire,
                                                   expect_retire_age=None):
        """
        step 1.1.2: 计算退休前一年的资产规模
            当前开始 到客户期望的退休年龄 每年的资产规模
            K年资产规模 = k-1年资产规模 * (1+通货膨胀) + (当年收入部分-当年支出部分)
        """
        expect_retire_age = expect_retire_age if expect_retire_age else self.expect_retire_age
        # 资产规模
        res_scale_show = {}
        if pension_everyyear_before_retire and cost_everyyear_before_retire and self.expect_retire_age:
            for k in range(self.current_age + 1, expect_retire_age):
                pension = pension_everyyear_before_retire.get(k, 0)
                if self.overlap_revenue and self.pension_age and self.expect_retire_age and \
                    k>=self.pension_age and k<self.expect_retire_age and self.expect_retire_age>self.pension_age:
                    pension = self.overlap_revenue.get(k,0)

                cost = cost_everyyear_before_retire.get(k, 0)
                revenue = res_scale_show.get(k - 1, current_pension * 12 - current_expenditure * 12)
                year_scale = (revenue * (1 + self.inflation_rate)) + (pension - cost)
                res_scale_show[k] = round(year_scale)
        # print("月薪:{},支出:{},资产规模：{}".format(current_pension,current_expenditure,res_scale_show))
        return res_scale_show

    def add_current_revenue(self, res_max_age_fund, cost_everyyear_before_retire):
        '''
        退休建议-增加退休前收入
        '''
        res_current_fund = {}
        current_revenue = self.total_revenue
        current_expenditure = self.total_expenditure
        flag = False
        if current_revenue is None:
            return -1, flag

        if cost_everyyear_before_retire:
            interval = 0.01
            fund_list = [i for i in np.arange(0.01, constants.MAX_CURRENT_INCOME_MONTHLY_RISE_TO*2, interval)]
            left, right = 0, len(fund_list)

            while True:
                mid = left + (right - left) // 2
                if mid >= len(fund_list):
                    return self.total_revenue * (1 + constants.MAX_CURRENT_INCOME_MONTHLY_RISE_TO), flag
                if right < 1:
                    return fund_list[1]

                current_revenue = self.total_revenue * (1 + round(fund_list[mid],2)) #current_revenue * (1 + self.inflation_rate)
                _, res_pension_everyyear_before_retire_show = self.prepare_pension_everyyear_before_retire(current_revenue)

                # 计算当前资产规模
                res_current_fund = self.compute_fund_scale_everyyear_before_retire(current_revenue, current_expenditure,
                                                                                   res_pension_everyyear_before_retire_show,
                                                                                   cost_everyyear_before_retire)
                if not res_current_fund or len(list(res_current_fund.keys())) <= 0:
                    flag = False
                    return -1, flag
                if res_current_fund.get(list(res_current_fund.keys())[-1]) >= res_max_age_fund:
                    '''
                    1.收入增长率30%一一 查行业数据：18%左右
                    '''
                    if ((
                                current_revenue - self.total_revenue) / self.total_revenue) > constants.MAX_CURRENT_INCOME_MONTHLY_RISE_TO:
                        logger.info('{} 收入增长率超过{}，不合理'.format(self.uid,constants.MAX_CURRENT_INCOME_MONTHLY_RISE_TO))
                        break
                    else:
                        logger.info('退休前收入提升：{}'.format( round(fund_list[mid],2) ))
                        flag = True
                        break

                if res_current_fund.get(list(res_current_fund.keys())[-1]) > res_max_age_fund:
                    right = mid - 1  # 收入增长率太高，需要降低
                else:
                    left = mid + 1 # 收入增长率太低，需要提高
        return current_revenue, flag

    def reduce_current_expenditure(self, res_max_age_fund, res_pension_everyyear_before_retire_show):
        '''
        退休建议-减少退休前支出
        '''
        current_revenue = self.total_revenue
        current_expenditure = self.total_expenditure
        flag = False
        if not res_max_age_fund or not res_pension_everyyear_before_retire_show:
            return -1, flag

        if res_pension_everyyear_before_retire_show:
            while True:
                current_expenditure = current_expenditure - 500

                '''
               2.支出下降一一目的是满足最低的基本生活花销
                   合理范围是：支出下降 【0-40%】
                   如果支出下降比例40%<全国最高的低保：1365 方案不科学
               '''
                if (self.total_expenditure - current_expenditure) / self.total_expenditure > constants.MAX_CURRENT_EXPENDITURE_MONTHLY_REDUCE_TO or (
                        (self.total_expenditure - current_expenditure) / self.total_expenditure <= constants.MAX_CURRENT_EXPENDITURE_MONTHLY_REDUCE_TO and
                        current_expenditure < constants.MIN_LIVING_ALLOWANCE
                ):
                    logger.info('{} 支出下降比例超过{}，不合理'.format(self.uid,
                                                                      constants.MAX_CURRENT_EXPENDITURE_MONTHLY_REDUCE_TO))
                    flag = False
                    break

                if current_expenditure < 0:
                    flag = False
                    current_expenditure = -1
                    break

                _, cost_show_before_retire = self.cost_everyyear_before_retire(current_expenditure)

                res_current_fund = self.compute_fund_scale_everyyear_before_retire(current_revenue, current_expenditure,
                                                                                   res_pension_everyyear_before_retire_show,
                                                                                   cost_show_before_retire)

                if not res_current_fund or len(list(res_current_fund.keys())) <= 0:
                    flag = False
                    current_expenditure = -1
                    break

                if res_current_fund.get(list(res_current_fund.keys())[-1]) >= res_max_age_fund:
                    flag = True
                    break

        return current_expenditure, flag

    def reduce_cost_everymonth_after_retire(self, res_fund_scale):
        '''
        退休建议-减少退休后支出
        '''
        retire_expense_monthly_reduce_to = self.retire_cost
        flag = False
        if not res_fund_scale:
            return -1, flag

        while True:
            retire_expense_monthly_reduce_to = retire_expense_monthly_reduce_to - 500
            '''
            4.降低退休生活支出：20%
            '''
            if (self.retire_cost - retire_expense_monthly_reduce_to) / self.retire_cost > constants.MAX_RETIRE_EXPENSE_MONTHLY_REDUCE_TO:
                logger.info('{} 退休后生活支出降低比例超过{}，不合理'.format(self.uid,
                                                                            constants.MAX_RETIRE_EXPENSE_MONTHLY_REDUCE_TO))
                flag = False
                break

            necessary_fund_scale = self.find_necessary_fund_scale_slidewindow(
                retire_expense_monthly_reduce_to=retire_expense_monthly_reduce_to)
            if res_fund_scale >= necessary_fund_scale:
                flag = True
                break

        return retire_expense_monthly_reduce_to, flag

    def add_investment_revenue_before_retire(self, res_max_age_fund):
        '''
        退休建议-增加退休前理财收益率
        '''
        income_growth_rate_rise_to = self.financial_management
        flag = False
        if not income_growth_rate_rise_to or income_growth_rate_rise_to == 0:
            return -1, flag
        while True:
            income_growth_rate_rise_to = income_growth_rate_rise_to + 0.05
            '''
           3.收益率：最多6%
           '''
            if income_growth_rate_rise_to > constants.MAX_CURRENT_INCOME_YEARLY_TO:
                logger.info('{} 收益率超过{}，不合理'.format(self.uid, constants.MAX_CURRENT_INCOME_YEARLY_TO))
                flag = False
                break

            necessary_fund_scale = self.prepare_fund_scale(self.expect_retire_age - self.current_age,
                                                           income_growth_rate_rise_to)
            if necessary_fund_scale >= res_max_age_fund:
                flag = True
                break
            elif necessary_fund_scale == -1:
                break
        return income_growth_rate_rise_to, flag

    def calculate_retirement_age_delayed(self, res_max_age_fund=None, cost_everyyear_before_retire=None):
        '''
        退休建议-延迟退休年龄
        '''
        res_current_fund = {}
        current_revenue = self.total_revenue
        current_expenditure = self.total_expenditure
        expect_retire_age = self.expect_retire_age
        flag = False
        if not self.expect_retire_age:
            return 0, flag
        if cost_everyyear_before_retire:
            while True:
                expect_retire_age += 1
                _, res_pension_everyyear_before_retire_show = self.prepare_pension_everyyear_before_retire(
                    current_revenue)

                # 计算当前资产规模
                res_current_fund = self.compute_fund_scale_everyyear_before_retire(current_revenue, current_expenditure,
                                                                                   res_pension_everyyear_before_retire_show,
                                                                                   cost_everyyear_before_retire,
                                                                                   expect_retire_age)
                if not res_current_fund:
                    break
                if self.pension_age and expect_retire_age < self.pension_age and res_current_fund.get(
                        list(res_current_fund.keys())[-1]) >= res_max_age_fund:
                    flag = True
                    break
                if res_current_fund.get(list(res_current_fund.keys())[-1]) < res_max_age_fund:
                    break
        return expect_retire_age, flag

    def retire_advice(self, res_max_age_fund=None):
        '''
        退休建议
        '''
        if not res_max_age_fund:
            res_max_age_fund = self.find_necessary_fund_scale_slidewindow()

        res_fund_scale = self.prepare_fund_scale(self.expect_retire_age - self.current_age)
        logger.info('{} step 1.2: 退休当年剩余资产规模 {}'.format(self.uid,str(res_fund_scale)) )

        if res_fund_scale:
            res_fund_scale = round(res_fund_scale) if res_fund_scale > 0 else 0

        # 退休前需储备资金不超过退休前资产规模，目标能达成，反之无法达成
        promote_plan = {}
        retire_plan_feasibility = True if res_max_age_fund <= res_fund_scale else False
        promote_plan['retire_plan_feasibility'] = retire_plan_feasibility
        logger.info('{} step 4.1 退休前资产规模:{},退休前应备资产规模:{}'.format(self.uid,res_fund_scale, res_max_age_fund))
        flag_all = False
        if res_max_age_fund > res_fund_scale:
            _, cost_show_before_retire = self.cost_everyyear_before_retire()

            _, res_pension_everyyear_show = self.prepare_pension_everyyear()

            _, res_pension_everyyear_before_retire_show = self.prepare_pension_everyyear_before_retire()
            res_pension_everyyear_show = {**res_pension_everyyear_before_retire_show, **res_pension_everyyear_show}

            # 1，增加当前收入
            current_income_monthly_rise_to, flag_one = self.add_current_revenue(res_max_age_fund,
                                                                                cost_show_before_retire)
            promote_plan['current_income_monthly_rise_to'] = round(current_income_monthly_rise_to)
            promote_plan['current_income_monthly_rise_to_flag'] = flag_one
            logger.info(
                '{} step 4.1.1: 退休前收入部分:{} 增加到：{} 是否可行:{}'.format(self.uid,self.total_revenue, round(current_income_monthly_rise_to),
                                                               flag_one))
            # 2，减少当前花销
            current_expenditure_monthly_reduce_to, flag_two = self.reduce_current_expenditure(res_max_age_fund,
                                                                                              res_pension_everyyear_before_retire_show)
            logger.info('{} step 4.1.2: 退休前花销部分:{} 减少到 {} 是否可行:{}'.format(self.uid,self.total_expenditure,
                                                                       current_expenditure_monthly_reduce_to, flag_two))
            promote_plan['current_expenditure_monthly_reduce_to'] = round(current_expenditure_monthly_reduce_to)
            promote_plan['current_expenditure_monthly_reduce_to_flag'] = flag_two
            # 3，减少退休后花销
            retire_expense_monthly_reduce_to, flag_three = self.reduce_cost_everymonth_after_retire(res_fund_scale)
            logger.info(
                '{} step 4.1.3: 退休后花销部分:{} 减少到 {} 是否可行:{}'.format(self.uid,self.retire_cost, retire_expense_monthly_reduce_to,
                                                               flag_three))
            promote_plan['retire_expense_monthly_reduce_to'] = round(retire_expense_monthly_reduce_to)
            promote_plan['retire_expense_monthly_reduce_to_flag'] = flag_three
            # 4，增加退休前的理财收入
            income_growth_rate_rise_to, flag_four = self.add_investment_revenue_before_retire(res_max_age_fund)
            income_growth_rate_rise_to = "%d%%" % (
                    income_growth_rate_rise_to * 100) if income_growth_rate_rise_to > 0 else income_growth_rate_rise_to
            logger.info(
                '{} step 4.1.4: 退休前的理财收益:{} 增加到 {} 是否可行:{}'.format(self.uid,self.financial_management, income_growth_rate_rise_to,
                                                                flag_four))
            promote_plan['income_growth_rate_rise_to'] = income_growth_rate_rise_to
            promote_plan['income_growth_rate_rise_to_flag'] = flag_four
            # 5. 延迟退休年龄
            retirement_age_delayed, flag_five = self.calculate_retirement_age_delayed(res_max_age_fund,
                                                                                      cost_show_before_retire)
            logger.info(
                '{} step 4.1.5: 预期退休年龄:{} 延迟到:{} 是否可行:{} '.format(self.uid,self.expect_retire_age, str(retirement_age_delayed),
                                                               flag_five))
            promote_plan['need_expect_retire_age'] = retirement_age_delayed
            promote_plan['need_expect_retire_age_flag'] = flag_five
            if flag_one or flag_two or flag_three or flag_four or flag_five:
                logger.info(
                    'step 4.1: 有方案可行 flag_one:{},flag_two:{},flag_three:{},flag_four:{},flag_five:{} '.format(flag_one,
                                                                                                              flag_two,
                                                                                                              flag_three,
                                                                                                              flag_four,
                                                                                                              flag_five))
                flag_all = True
            else:
                logger.info('综合方案')
                # 综合方案可行
                if flag_all:
                    flag_all = True

        return promote_plan, flag_all


    # ============增额寿===========
    def calculate_final_balance(self, annual_withdrawal, annual_deposit):
        balance = 0
        year = self.current_age
        annual_deposit_val = None
        annual_withdrawal_val = None

        for year in range(self.current_age, self.max_age+1):
            if year >= self.current_age and year < (self.current_age+10):
                annual_deposit_val = annual_deposit
            else:
                annual_deposit_val = 0

            if year >= self.expect_retire_age and year <= self.max_age:
                annual_withdrawal_val = annual_withdrawal
            else:
                annual_withdrawal_val = 0
            balance = balance * (1 + self.financial_management) + annual_deposit_val - annual_withdrawal_val
            balance = round(balance)

            if balance <= 0:
                return balance, year

        return balance, year

    def calculate_annual_withdrawal(self):
        annual_deposit = round((self.total_revenue - self.total_expenditure) * 12 * 0.4)
        final_balance = annual_deposit
        try:
            fund_list = [i for i in np.arange(100, annual_deposit, 100)]
            left, right = 0, len(fund_list)

            while left < right:
                mid = (left + right) // 2

                total_receive = fund_list[mid]

                final_balance, year = self.calculate_final_balance(total_receive, annual_deposit)

                print('year:{},mid:{},annual_deposit:{},total_receive:{},final_balance:{}'.format(year, mid,
                                                                                                  annual_deposit,
                                                                                                  total_receive,
                                                                                                  final_balance))

                if final_balance <= 0 and year == self.max_age:
                    return total_receive,annual_deposit

                if final_balance <= 0 and year < self.max_age:
                    right = mid
                else:
                    left = mid + 1


        except Exception as e:
            logger.error(traceback.format_exc())

        return final_balance,annual_deposit
    # =============增额寿==========
    def product_advice(self):
        if self.total_revenue is not None and self.total_expenditure is not None \
                and self.retire_cost is not None and self.current_age is not None:
            receive_year,storage_year = self.calculate_annual_withdrawal()
            logger.info('total_receive:{},total_storage:{}'.format(receive_year,storage_year))

            total_receive_dict = {}
            for ageNum in range(self.expect_retire_age,self.max_age):
                total_receive_dict[ageNum] = receive_year
            return total_receive_dict,storage_year
        else:
            return None, None

    # 退休金累计缺口
    def gap_fund_accumulate(self):
        gapFundAccumulate = None
        try:
            if self.expect_retire_age:
                # 预计退休年龄 到假定生命周期 每年的花销
                _, res_cost_show = self.cost_everyyear()

                # 退休金收入（法定退休年龄到 假定生命周期）
                _, res_pension_everyyear_show = self.prepare_pension_everyyear()

                for i in range(len(res_cost_show)):
                    k = i + self.expect_retire_age
                    cost = res_cost_show.get(k, 0)
                    pension = res_pension_everyyear_show.get(k, 0)

                    if self.overlap_revenue and self.pension_age and self.expect_retire_age and \
                            self.expect_retire_age > self.pension_age and k>=self.pension_age and k<self.expect_retire_age:
                            pension = self.overlap_revenue.get(k, 0)

                    if gapFundAccumulate is None:
                        gapFundAccumulate = 0

                    if (cost - pension) > 0:
                        gapFundAccumulate += (cost - pension)

                if gapFundAccumulate and gapFundAccumulate < 0:
                    gapFundAccumulate = 0

                gapFundAccumulate = round(gapFundAccumulate)
        except Exception as ee:
            pass
        logger.info('{} step 4.1.1: 退休金累计缺口:{}'.format(self.uid,gapFundAccumulate))
        return gapFundAccumulate

    # 退休前年盈余
    def before_retire_surplus(self):
        totalSurplus = None
        yearSurplus = {}
        try:
            _, res_pension_everyyear_before_retire_show = self.prepare_pension_everyyear_before_retire()
            _, cost_show_before_retire = self.cost_everyyear_before_retire()

            if len(cost_show_before_retire)>0 and len(res_pension_everyyear_before_retire_show)>0:
                for i in range(len(cost_show_before_retire)):
                    k = i + self.current_age
                    cost = cost_show_before_retire.get(k, 0)
                    pension = res_pension_everyyear_before_retire_show.get(k, 0)

                    if self.overlap_revenue and self.pension_age and self.expect_retire_age and \
                            k >= self.pension_age and k < self.expect_retire_age and self.expect_retire_age > self.pension_age:
                        pension = self.overlap_revenue.get(k, 0)

                    if totalSurplus is None:
                        totalSurplus = 0
                    totalSurplus += (pension - cost)
                    yearSurplus[i + self.current_age] = (pension - cost)
        except Exception as ee:
            pass
        return totalSurplus, yearSurplus


    def finance_trends(self,total_receive_dict):
        list_detailsVos = []
        if self.expect_retire_age:
            _, prepare_everyyear_before_retire_show = self.prepare_pension_everyyear_before_retire()
            _, cost_show_before_retire = self.cost_everyyear_before_retire()

            _, res_pension_everyyear_show = self.prepare_pension_everyyear()
            _, res_cost_show = self.cost_everyyear()

            cost_show_before_retire = {**cost_show_before_retire, **res_cost_show}
            all_prepare = {}
            if self.overlap_revenue:
                for ageNum in range(self.current_age,self.max_age):
                    if not prepare_everyyear_before_retire_show.get(ageNum) and \
                            not res_pension_everyyear_show.get(ageNum) and \
                        not self.overlap_revenue.get(ageNum):
                        continue

                    if ageNum<self.pension_age:
                        all_prepare[ageNum] = prepare_everyyear_before_retire_show.get(ageNum,0)
                    elif ageNum >=self.pension_age and ageNum<self.expect_retire_age:
                        all_prepare[ageNum] = self.overlap_revenue.get(ageNum, 0)
                    else:
                        all_prepare[ageNum] = res_pension_everyyear_show.get(ageNum, 0)
            else:
                all_prepare = {**prepare_everyyear_before_retire_show, **res_pension_everyyear_show}
                if self.pension_age and self.expect_retire_age and self.expect_retire_age<self.pension_age and self.total_revenue:
                    for ageNum in range(self.expect_retire_age, self.pension_age):
                        if not all_prepare.get(ageNum):
                            all_prepare[ageNum] = 0

            for i in range(self.current_age, self.max_age + 1):
                if not cost_show_before_retire.get(i, None) and not all_prepare.get(i, None):
                    continue

                oneline = {'age': i}
                if cost_show_before_retire.get(i) is not None:
                    oneline['livingCost'] = cost_show_before_retire.get(i)
                if all_prepare.get(i) is not None:
                    if total_receive_dict and total_receive_dict.get(i,0):
                        oneline['pensionPrepare'] = all_prepare.get(i)+total_receive_dict.get(i,0)
                    else:
                        oneline['pensionPrepare'] = all_prepare.get(i)
                elif all_prepare.get(i) is None and cost_show_before_retire.get(i) is not None \
                        and i>=self.expect_retire_age and i<self.pension_age:
                    oneline['pensionPrepare'] = 0


                list_detailsVos.append(oneline)
        return list_detailsVos

    def count_overlap_revenue(self):
        if self.pension_age and self.expect_retire_age and self.expect_retire_age>self.pension_age \
                and self.total_revenue_after_retire and self.total_revenue_before_retire and not self.overlap_revenue:
            overlap_revenue = {}
            for ageNum in range(self.pension_age,self.expect_retire_age):
                overlap_revenue[ageNum] = self.total_revenue_before_retire[ageNum] + self.total_revenue_after_retire[ageNum]
            self.overlap_revenue = overlap_revenue

    def build_solution(self):
        logger.info('{} step 0.0: 用户信息: {}'.format(self.uid, str(self.user_info)))
        all_ok = False
        try:
            all_ok = self.check_param()  # 参数合法性校验
        except Exception as e:
            logger.error('{} step 0.0: 参数合法性校验失败, 请检查参数是否合法,{}'.format(self.uid,traceback.format_exc()))
        if not all_ok:
            logger.info('{} step 0.0: 参数合法性校验失败, 请检查参数是否合法'.format(self.uid))
            return {}

        detail_info = {'summaryVo': {}}
        total_receive_dict = {}

        res_max_age_fund = None
        if self.retire_cost:
            res_max_age_fund = self.find_necessary_fund_scale_slidewindow()

        res_pmt = None
        if res_max_age_fund is not None:
            res_pmt = -self.compute_PMT(-self.retire_reserve_fund, res_max_age_fund, 1)
            logger.info('{} step 3.0: 退休前每年应该存储金额 {}'.format(self.uid, round(res_pmt)))

        if res_pmt and res_pmt<=1000:
            detail_info["promote_plan"] = {}
            detail_info["promote_plan"]['retire_plan_feasibility'] = True
        else:
            gap_fund_accumulate = self.gap_fund_accumulate()

            if gap_fund_accumulate:
                detail_info['summaryVo']["gap_fund_accumulate"] = gap_fund_accumulate  # 退休资金累积缺口 ok
            if res_pmt:
                detail_info['summaryVo']["yearly_income_surplus"] = round(res_pmt)  # 退休前每年应该存储金额 ok
            if res_max_age_fund is not None:
                detail_info['summaryVo']["res_max_age_fund"] = res_max_age_fund  # 退休前需存储资金（倒推出来）

            if self.total_revenue and self.total_revenue > 0 and \
                    self.total_expenditure and self.total_expenditure > 0 and \
                    self.expect_retire_age and self.expect_retire_age>self.current_age and \
                    self.retire_cost and self.retire_cost>0:
                promote_plan, promote_plan_flag = self.retire_advice(res_max_age_fund)
                detail_info["promote_plan"] = promote_plan
                detail_info["promote_plan_flag"] = promote_plan_flag

        # 退休前总盈余 退休前各年盈余
        res_surplus, yearSurplus = self.before_retire_surplus()
        if res_surplus:
            detail_info["summaryVo"]["res_surplus"] = round(res_surplus)  # 退休前收入盈余 ok

        list_detailsVos = self.finance_trends(total_receive_dict)
        for detailsVos_info in list_detailsVos:
            if detailsVos_info.get('age') == self.expect_retire_age:
                self.expect_retire_age_detail = detailsVos_info
            if detailsVos_info.get('age') == self.pension_age:
                self.pension_age_detail = detailsVos_info

        detail_info["detailsVos"] = list_detailsVos
        detail_info["user_info"] = self.user_info

        logger.info('{} 计算结果：{}'.format(self.uid,detail_info))

        if (not list_detailsVos or len(list_detailsVos)==0) or not detail_info.values():
            return {}
        else:
            detail_info["summaryVo"]["legal_retirement_age"] = self.pension_age,  # 法定退休年龄 ok
            detail_info["summaryVo"]["retire_reserve_fund"] = self.retire_reserve_fund # 当前存款 ok
            return detail_info

    # 将生成的方案通过长链接发送给前端
    def push_solution_to_front(self, clientName, solution_result):

        dir_path = IMG_PATH + clientName + "/"
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)
        img_path = dir_path + str(uuid.uuid4()).replace("-", "") + ".png"

        logger.info('{} DrawPicture Start!!! {}'.format(self.uid,img_path))
        # 按照计算生成图片
        saveResult = DrawPicture.save_financial_trend(solution_result, img_path)
        logger.info('{} DrawPicture End!!!'.format(self.uid))

        suffix = img_path.split('/')[-1]
        ok = clientName + "/" + suffix
        img_url = self.oss.upload_file(ok, img_path)
        logger.info('{} 上传oss成功!!! {}'.format(self.uid,img_url))

        send_conversation_img_message = MessageContent(constants.MESSAGE_TYPE_IMG, img_url)
        send_conversation_solution_message = MessageContent(constants.MESSAGE_TYPE_SOLUTION, solution_result)

        messages = [send_conversation_img_message, send_conversation_solution_message]
        return messages

    # 将用户信息通过长链接发送给前端
    async def push_user_info_to_front(self, user_info_show):
        send_userinfo_solution_message = MessageContent(constants.MESSAGE_TYPE_SOLUTION, user_info_show)
        messages = [send_userinfo_solution_message]
        return messages

    async def build_advice_insurance(self):
        logger.info('{} step 0.0: 用户信息: {}'.format(self.uid, str(self.user_info)))

        self.solution_info = ""
        solution_messages = []

        if self.expect_retire_age is None or self.current_age is None \
                or self.total_revenue is None or self.total_expenditure is None:
            logger.info('{} step 0.1: 信息不全，无法计算'.format(self.uid))
            return "", {}
        elif (self.expect_retire_age - self.current_age) <=10:
            logger.info('{} step 0.2: 当前离退休不到10年，不计算'.format(self.uid))
            return "", {}
        try:
            logger.info('{} step 1: 计算增额寿数据'.format(self.uid))
            total_receive_dict, total_storage = self.product_advice()
            if not total_receive_dict:
                logger.info('{} step 0.3: 无法计算增额寿年领取金额'.format(self.uid))
                return None, None

            new_summaryVo = {}
            new_summaryVo['everyyear_surplus'] = round(total_storage)  # 年结余
            new_summaryVo['everyyear_storage'] = round(total_storage)  # 年存储
            new_summaryVo['total_year'] = 10  # 定存年限
            new_summaryVo['everyyear_receive'] = round(total_receive_dict.get(self.expect_retire_age))  # 年支取
            new_summaryVo['total_receive'] = round(total_receive_dict.get(self.expect_retire_age) * (self.max_age - self.expect_retire_age))  # 累计领取
            logger.info('{} step 0.4: 计算增额寿年领取金额成功,{}'.format(self.uid,new_summaryVo))

            if new_summaryVo:
                self.solution_info = "基于你当前每年的结余情况还不错，我们建议将结余的钱根据用途做多份拆解" \
                                "1、5%左右先给家人买份保险既防止意外，又有收益" \
                                "2、10%在银行存一部分活期，作为生活备用金，以备急用" \
                                "3、40%可以投资在稳健性理财产品上" \
                                "4、15%用于高收益 (高风险) 投资，如: 股票等" \
                                "5、20%用于银行定期存款" \
                                "6、剩余的10%作为不可预测消费或投资流动资金"

                try:
                        if new_summaryVo and 'everyyear_storage' in new_summaryVo:
                            total_year = new_summaryVo.get('total_year', 0)

                            # 总存储金额
                            total_storage_str = ''
                            # 每年存储金额
                            everyyear_storage_str = ''
                            everyyear_storage = new_summaryVo.get('everyyear_storage', 0)
                            if everyyear_storage > 10000:
                                everyyear_storage = round(everyyear_storage / 10000,2)
                                everyyear_storage_str += str(everyyear_storage) + '万元'
                                total_storage_str += str(round((total_storage / 10000)*total_year,2)) + '万元'
                            else:
                                everyyear_storage_str += str(everyyear_storage) + '元'
                                total_storage_str += str(total_storage*(total_year)) + '元'

                            # 总领取金额
                            total_receive_str = ''
                            #年领取金额
                            everyyear_receive_str = ''
                            everyyear_receive = new_summaryVo.get('everyyear_receive', 0)
                            if everyyear_receive > 10000:
                                everyyear_receive = round(everyyear_receive / 10000,2)
                                everyyear_receive_str = str(everyyear_receive) + '万元'
                                total_receive_str = str(round(everyyear_receive*(self.max_age - self.expect_retire_age),2)) + '万元'
                            else:
                                everyyear_receive_str = str(everyyear_receive) + '元'
                                total_receive_str = str(everyyear_receive*(self.max_age - self.expect_retire_age)) + '元'


                            # self.solution_info += "在向用户说明已计算出来的养老财务相关结果基础上，结合用户的退休目标，退休方案可行性：不可行。因此需要提供给用户的建议：\n"
                            self.solution_info += "那其实也提到这类寿险产品兼顾风险和收益。考虑到增额终身寿这类产品是中长期的，" \
                                                  "所以建议将40%年结余购买该类产品。如果按每年"+everyyear_storage_str+"做"+str(total_year)+"年期的投入，" \
                                                  "总共投入"+total_storage_str+"。在"+str(round(self.expect_retire_age))+"岁时，作为养老收入取出，" \
                                                   "每年可领"+everyyear_receive_str+"直至生命周期（假定生命周期为"+str(round(self.max_age))+"岁），" \
                                                  "总共可领取"+total_receive_str+"，收益率可达到3%。既保证了收益，同时也增加了养老后入。同时在保单有效期内，" \
                                                  "如果出现全残或身故的情况，还可获得一次性支付的赔付金。(必须讲解该部分建议)\n"

                            logger.info("self.solution_info-{}".format(self.solution_info))
                except Exception as e:
                    logger.exception(e)
                    logger.info("构建建议说明失败")
            user_info_messages = await self.push_user_info_to_front(new_summaryVo)
            loop = asyncio.get_event_loop()
            solution_messages.extend(user_info_messages)
            logger.info("self.solution_info-{}".format(self.solution_info))
        except Exception as e:
            # 打印错误堆栈
            logger.exception(e)
            logger.info("构建方案失败")

        return self.solution_info, solution_messages

    async def build_retirement_plan(self, user_info_updated=True):
        self.solution_info = ""
        solution_messages = []
        try:
            solution_result = self.build_solution()
            if not solution_result:
                return None, None
            self.solution_result = solution_result
            summaryVo = solution_result.get("summaryVo", {})
            promote_plan = solution_result.get("promote_plan", {})
            new_summaryVo = {}
            # 把summaryVo中的key转换成中文
            for key in summaryVo.keys():
                if key in self.plan_summary_dict.keys():
                    new_summaryVo[self.plan_summary_dict[key]] = summaryVo[key]
            if new_summaryVo:
                new_summaryVo_str = ""
                for key, value in new_summaryVo.items():
                    if key in ["退休资金累积缺口", "退休前需存储资金", "每年收入需要结余", "退休前收入盈余"]:
                        if value > 10000:
                            value = str(value) + "元(" + str(round(value / 10000)) + "万元)"
                        else:
                            value = str(value) + "元"

                    new_summaryVo_str += key + ":" + str(value) + "，"

                user_info_cn_str = ""
                for key, value in self.user_info_cn.items():
                    user_info_cn_str = user_info_cn_str + key + ":" + str(value) + "，"

                self.solution_info = "基于您提供的信息，"

                self.solution_info = self.solution_info + "已计算出来的养老财务相关结果（计算基于的假定中国人的平均寿命是"+str(constants.DEFAULT_MAX_LIVE_AGE)+"岁，通货膨胀率平均为"+str(round(constants.INFLATION_RATE*100))+"%、理财收益率"+str(round(constants.DEFAULT_FINANCIAL_MANAGEMENT*100))+"%）" \
                                                              "及已经通过图片给用户说明情况，你需要根据图中信息讲解给用户（图中蓝色的点代表的用户每个月的应备资金即支出情况，" \
                                                              "橙色的点代表已备资金即收入情况），"
                res_surplus = new_summaryVo.get("退休前收入盈余", 0)
                if res_surplus > 0 or ("退休资金累积缺口" not in new_summaryVo.keys()):
                    self.solution_info += "橙色阴影的面积代表用户的结余,"

                if self.pension_age > self.expect_retire_age:
                    self.solution_info = self.solution_info + "在用户期望退休那年"+ str(self.expect_retire_age_detail["age"]) + "岁，用户的退休支出为" + str(self.expect_retire_age_detail["livingCost"]) + "元，"

                self.solution_info = self.solution_info + "在法定退休那年"+ str(self.pension_age_detail["age"]) + "岁，用户的退休那年支出为" + str(self.pension_age_detail["livingCost"]) + "元，"
                if self.pension_age_detail["pensionPrepare"] > 0:
                    self.solution_info = self.solution_info + "退休后每年的通过社保产生的收入为" + str(self.pension_age_detail["pensionPrepare"]) + "元，"
                    # if (self.expect_retire_age - self.start_security_age) >= 15:
                    #     self.solution_info = self.solution_info + "退休后每年的通过社保产生的收入为" + str(self.pension_age_detail["pensionPrepare"]) + "元，"
                    # else:
                    #     self.solution_info = self.solution_info + "在用户期望退休那年"+ str(self.expect_retire_age_detail["age"]) + "岁，交社保不满15年，只能在法定退休那年"+ str(self.pension_age_detail["age"]) + "岁，一次性领取个人养老金" + str(
                    #         self.pension_age_detail["pensionPrepare"]) + "元，"

                if "退休资金累积缺口" in new_summaryVo.keys():
                    self.solution_info = self.solution_info + "蓝色阴影的面积代表用户的缺口,具体退休资金累积缺口计算的结果:" + new_summaryVo_str + "，请根据图中信息和计算出来的结果讲解给用户。"
                    if not self.total_revenue:
                        self.solution_info = self.solution_info + "对于退休后的有资金缺口的用户引导其提供当前的收入和支出情况，用于进一步给出有针对性的建议。"
                    # self.solution_info = self.solution_info + "蓝色阴影的面积代表用户的缺口,具体退休资金累积缺口计算的结果:" + new_summaryVo_str + "，请根据图中信息和计算出来的结果讲解给用户。"
                    # if not self.total_expenditure or self.total_expenditure==-1:
                    #     self.solution_info = self.solution_info + "对于退休后的有资金缺口的用户引导其提供当前的支出情况，用于进一步给出有针对性的建议。"


                try:
                    if promote_plan and 'retire_plan_feasibility' in promote_plan:
                        if not promote_plan['retire_plan_feasibility']:
                            self.solution_info += "在向用户说明已计算出来的养老财务相关结果基础上，结合用户的退休目标，退休方案可行性：不可行。为了确保用户方案的可行，提供给用户的建议：\n"
                            if promote_plan['current_income_monthly_rise_to_flag']:
                                self.solution_info += "方案:{expect_retire_age}岁退休的目标可以达成，不过未来每个月需提升收入到{current_income_monthly_rise_to}元。\n".format(
                                    **promote_plan, **self.user_info)
                            if promote_plan['current_expenditure_monthly_reduce_to_flag']:
                                self.solution_info += "方案:{expect_retire_age}岁退休的目标可以达成，但每个月花销需从{total_expenditure}元减少到{current_expenditure_monthly_reduce_to}元。\n".format(
                                    **promote_plan, **self.user_info)
                            if promote_plan['retire_expense_monthly_reduce_to_flag']:
                                self.solution_info += "方案:{expect_retire_age}岁退休的目标可以达成,但退休后每个月的花销需从{retire_cost}元减少至{retire_expense_monthly_reduce_to}元。\n".format(
                                    **promote_plan, **self.user_info)
                            if promote_plan['need_expect_retire_age_flag']:
                                self.solution_info += "方案:您的期望退休年龄需从{expect_retire_age}岁调整到{need_expect_retire_age}岁。\n".format(
                                    **promote_plan, **self.user_info)

                            if (self.expect_retire_age-self.current_age)>=10:
                                self.solution_info += '其他建议：在当前银行利率持续走低的大环境下，建议将手上的结余资金再次做投资增值，增加被动收入，应对未来的风险。'

                        else:
                            if (self.expect_retire_age - self.current_age) >= 10:
                                self.solution_info += "退休没有缺口，退休方案是可行的（告知用户）。"
                                self.solution_info += '建议：在当前银行利率持续走低的大环境下，建议将手上的结余资金再次做投资增值，增加被动收入，应对未来的风险。' \
                                                      '对于投资类的产品，我们通常会考虑收益率、投资风险，准入门槛、资金调配灵活性等。'\
                                '结合你的情况，可以选择一些投资风险较低，收益率稳定的产品，如银行理财产品、债券基金、货币基金、理财型保险等。'
                            else:
                                self.solution_info += "退休没有缺口，退休方案是可行的。"
                except Exception as e:
                    logger.exception(e)
                    logger.info("构建解决方案说明失败")

            if user_info_updated:
                user_info_messages = await self.push_user_info_to_front(new_summaryVo)
                loop = asyncio.get_event_loop()
                # ！！！这里是阻塞型IO，用run_in_executor
                solution_other_messages = await loop.run_in_executor(None,
                                                                     self.push_solution_to_front,
                                                                     self.session_id,
                                                                     solution_result)
                solution_messages.extend(user_info_messages)
                solution_messages.extend(solution_other_messages)

        except Exception as e:
            # 打印错误堆栈
            logger.exception(e)
            logger.info("构建方案失败")

        return self.solution_info, solution_messages


def user_info_to_send(clientName, user_info_show):
    info_list = []
    for key, value in user_info_show.items():
        info_item = {"displayName": key, "value": value}
        info_list.append(info_item)

    user_info_show = {"clientType": "ydt", "clientName": clientName, "msgType": "ydt",
                      "data": {"code": 100,
                               "info": info_list
                               }
                      }
    return user_info_show

def printPromotePlan(solution_planner,solution_result):
    if solution_result.get('promote_plan',None):
        logStr = '当前是否符合退休预期：{}\n'.format(solution_result["promote_plan"].get('retire_plan_feasibility'))

        logStr += '退休前收入 {} 增加到：{},可行性：{}\n'.format(solution_planner.total_revenue,
                                                                  solution_result['promote_plan'].get('current_income_monthly_rise_to', '-'),
                                                                  solution_result['promote_plan'].get('current_income_monthly_rise_to_flag', '-'))

        logStr += '退休前支出 {} 减少到：{},可行性：{}\n'.format(solution_planner.total_expenditure,
                                                        solution_result['promote_plan'].get('current_expenditure_monthly_reduce_to', '-'),
                                                        solution_result['promote_plan'].get('current_expenditure_monthly_reduce_to_flag', '-'))

        logStr += '退休前理财收益率 {} 增加到：{},可行性：{}\n'.format(solution_planner.financial_management,
                                                        solution_result['promote_plan'].get('income_growth_rate_rise_to', '-'),
                                                        solution_result['promote_plan'].get('income_growth_rate_rise_to_flag', '-'))

        logStr += '退休后支出 {} 减少到：{},可行性：{}\n'.format(solution_planner.retire_cost,
                                                                  solution_result['promote_plan'].get(
                                                                      'retire_expense_monthly_reduce_to', '-'),
                                                                  solution_result['promote_plan'].get(
                                                                      'retire_expense_monthly_reduce_to_flag', '-')
                                                                  )
        logStr += '退休年龄 {} 延迟到：{},可行性：{}\n'.format(solution_planner.expect_retire_age,
                                                                  solution_result['promote_plan'].get(
                                                                      'need_expect_retire_age', '-'),
                                                                  solution_result['promote_plan'].get(
                                                                      'need_expect_retire_age_flag', '-')
                                                                  )
        logger.info(logStr)

if __name__ == "__main__":
    # parser = argparse.ArgumentParser()
    # parser.add_argument("--clientName", type=str, default="UoFBMRNqbKC5CsdcPOXZ")
    # args = parser.parse_args()
    # clientName = args.clientName

    # collect1 = main_local()
    # print(collect1)


    # data =  {'current_age': 45, 'expect_retire_age': 65, 'retire_cost': 12000, 'total_revenue': 5000, 'retire_reserve_fund': 300000, 'life_phases': '子女离家期', 'total_expenditure': 10000}
    # data = {'current_age': 35, 'expect_retire_age': 50, 'retire_cost': 10000, 'sex': '女', 'have_social_security': '是', 'start_security_num': 12, 'total_revenue': 20000, 'total_expenditure': 15500}
    # 缺口小
    # data = {'current_age': 40, 'expect_retire_age': 65, 'sex': '女', 'have_social_security': '是', 'start_security_num': 15,
    #         'total_revenue': 4167, 'retire_reserve_fund': 200000, 'life_phases': '子女离家期', 'total_expenditure': 2500}
    '''
    testJsons = None
    testDataPath = '/Users/yuwf/Downloads/model/GPT/LLM_master/LLM_internal/internal_server/agent/insurance_planner_gpt/test_data.json'

    with open(testDataPath, "r") as f:
        testJsons = f.readlines()

    if testJsons:
        for index,testJson in enumerate(testJsons):
            logger.info('{}/{}\n{}'.format(index, len(testJsons), testJson))
            if isinstance(eval(testJson) ,dict):
                data = eval(testJson)
            else:
                data = eval(testJson)[0]
            solution_planner = Solution(
                # {
                #   'current_age': 30, # 当前年龄
                #  'expect_retire_age': 60, #预计退休年龄
                #  'retire_cost': 5000, #退休后支出
                #  'sex': '男',#性别
                #  'have_social_security': '是', #是否缴纳社保
                #  # 'start_security_age': 25, #开始交社保年龄
                #  'start_security_num': 5, #交社保年限
                #  # 'total_revenue': 34167, #退休前收入
                #  # 'total_expenditure': 15000, #退休前支出
                #  # 'inflation_rate': 0, #通胀
                #  # 'financial_management':0, #理财收益率
                #  # 'persion_rise_rate': 0.1, #养老金涨幅
                #  # 'max_age': 85, #预计生命周期
                #  # 'retire_reserve_fund': 300000 #为退休准备的资金
                #  }
                data
                , "b6S4ogp3G6u2x5hrq2hW{}".format(index))
            solution_result = solution_planner.build_solution()
            if solution_result:
                logger.info('{}/{}\n'
                            '法定退休年龄:{}\n'
                            '退休资金累积缺口:{}\n'
                            '退休前需存储资金:{}\n'
                            '退休前每年应该存储金额:{}\n'
                            '退休前收入盈余:{}\n'.format(index,len(testJsons),
                    solution_result['summaryVo'].get('legal_retirement_age','-' ),
                                                               solution_result['summaryVo'].get('gap_fund_accumulate','-' ),
                                                               solution_result['summaryVo'].get('res_max_age_fund','-' ),
                                                               solution_result['summaryVo'].get('yearly_income_surplus','-' ),
                                                               solution_result['summaryVo'].get('res_surplus','-')))

                printPromotePlan(solution_planner, solution_result)
            else:
                logger.info('solution_result--{}'.format(solution_result))
    

    # 按照计算生成图片
    # DrawPicture.save_financial_trend(solution_result, '/Users/yuwf/Downloads/model/GPT/LLM_master/LLM_internal/internal_server/financial_trend.png')

    '''
    # data =  {'current_age': 56, 'sex': '男', 'total_revenue': 25000, 'total_expenditure': 16666,
    #          'expect_retire_age': 60, 'is_married': '是', 'life_phases': '子女离家期',
    #          'retire_cost': 16700, 'have_social_security': '是', 'start_security_age': 23,
    #          'start_security_num': 33, 'city': '杭州', 'monthly_payment_base': 6000,
    #          'remaining_mortgage_loan_amount': 0, 'children_num': 2, 'retire_reserve_fund': 200000}
    # data = {'current_age': 28, 'sex': '男', 'start_security_num': 5, 'expect_retire_age': 50,
    #         'retire_cost': 10000, 'have_social_security': '是', 'total_revenue': 15000}
    data  = {'current_age': 30, 'sex': '男', 'expect_retire_age': 50, 'retire_cost': 20000,
            'total_revenue': 25000, 'have_social_security': '是', 'start_security_age': 20, 'start_security_num': 10,'total_expenditure':5000}
    solution_planner = Solution(
        # {
        #   'current_age': 30, # 当前年龄
        #  'expect_retire_age': 60, #预计退休年龄
        #  'retire_cost': 5000, #退休后支出
        #  'sex': '男',#性别
        #  'have_social_security': '是', #是否缴纳社保
        #  # 'start_security_age': 25, #开始交社保年龄
        #  'start_security_num': 5, #交社保年限
        #  # 'total_revenue': 34167, #退休前收入
        #  # 'total_expenditure': 15000, #退休前支出
        #  # 'inflation_rate': 0, #通胀
        #  # 'financial_management':0, #理财收益率
        #  # 'persion_rise_rate': 0.1, #养老金涨幅
        #  # 'max_age': 85, #预计生命周期
        #  # 'retire_reserve_fund': 300000 #为退休准备的资金
        #  }
        data
        , "b6S4ogp3G6u2x5hrq2hW")
    # a,b = solution_planner.product_advice()
    # print('支取：{},存储：{}'.format(a,b))
    solution_result = solution_planner.build_solution()
    # solution_info, solution_other_messages = asyncio.run(solution_planner.build_retirement_plan())
    # logger.info('solution_result--{}'.format(solution_info))
    if solution_result:
        logger.info('法定退休年龄:{}\n'
                    '退休资金累积缺口:{}\n'
                    '退休前需存储资金:{}\n'
                    '退休前每年应该存储金额:{}\n'
                    '退休前收入盈余:{}\n'.format(
                                                 solution_result['summaryVo'].get('legal_retirement_age', '-'),
                                                 solution_result['summaryVo'].get('gap_fund_accumulate', '-'),
                                                 solution_result['summaryVo'].get('res_max_age_fund', '-'),
                                                 solution_result['summaryVo'].get('yearly_income_surplus', '-'),
                                                 solution_result['summaryVo'].get('res_surplus', '-')))

        printPromotePlan(solution_planner, solution_result)
    else:
        logger.info('solution_result--{}'.format(solution_result))
